Categories
Node.js Best Practices

Node.js Best Practices — Security Attacks

Spread the love

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.

In this article, we’ll look at some basic security practices to be aware of writing Node apps.

Prevent Query Injection Vulnerabilities with ORM/ODM Libraries

We should never pass in user-inputted strings straight into our app to prevent SQL or NoSQL injection attacks. Inputs should be validated and sanitized before being passed into database queries.

All reputable data access libraries like Sequelize, Knex, and Mongoose have built-in protection against script injection attacks.

Unsanitized strings can easily destroy data and expose them to unauthorized parties if they’re left unsanitized.

Collection of Generic Security Best Practices

We should keep up-to-date with general security best practices so we can implement them when we’re developing and running apps.

Adjust the HTTP Response Headers for Enhanced Security

We can use modules like helmet to secure headers to prevent attacks from using common attacks like cross-site scripting with our apps.

To add helmet and use it, we run:

npm i helmet

and then use it as follows:

const express = require('express');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const app = express();
app.use(helmet());

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => {
  res.send('hello');
});

app.listen(3000, () => console.log('server started'));

Helmet automatically protects us from cross-site scripting, enables strict transport security, and keep clients from sniffing the MIME types from responses.

The X-Powered-By header is also removed from the response so that attackers won’t know that our app is an Express app.

Constantly and Automatically Inspect for Vulnerable Dependencies

We can use npm audit or snyk to check for packages with vulnerable dependencies before going to production. Otherwise, attacks may take advantage of the vulnerabilities to commit attacks.

Avoid Using the Node.js crypto Library for Handling Passwords, use Bcrypt

bcrypt provides hash and salt functionality. Therefore it’s better for handling secrets than the built-in crypto library. It’s also faster.

We don’t want attackers to be able to brute-force passwords and tokens with dictionary attacks.

Escape HTML, JS and CSS Output

We should escape these kinds of code so that attacks can’t run malicious client-side code with our app. Dedicated libraries can explicitly mark the data as pure content and should never be executed.

Validate Incoming JSON Schemas

JSON schemas should be validated to make sure that the income request payload has valid data. For instance, we can use the jsonschema library to validate the structure and values of the JSON that’s sent.

We can use the jsonschema library as follows with an Express app:

const express = require('express');
const bodyParser = require('body-parser');
const Validator = require('jsonschema').Validator;
const v = new Validator();
const app = express();

const addressSchema = {
  "id": "/SimpleAddress",
  "type": "object",
  "properties": {
    "address": { "type": "string" },
  },
  "required": ["address"]
};

const schema = {
  "id": "/SimplePerson",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "address": { "$ref": "/SimpleAddress" },
  },
  "required": ["name", "address"]
};

v.addSchema(addressSchema, '/SimpleAddress');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post('/person', (req, res) => {
  if (v.validate(req.body, schema).errors.length) {
    return res.send(400)
  }
  res.send('success');
});

app.listen(3000, () => console.log('server started'));

In the code above, we required the jsonschema library and use its validator. Then we defined the /SimpleAddress schema, which is referenced by the /SimplePerson schema.

We add the /SimpleAddress schema with:

v.addSchema(addressSchema, '/SimpleAddress');

to reference it in /SimplePerson .

Then we can check our request body against our schema with:

v.validate(req.body, schema).errors.length

Then we stop the request from proceeding if the request body fails validation.

Support blacklisting JWTs

JSON Web Tokens (JWTs) that were used for malicious user activity should be revoked. Therefore, our app needs a way to revoke these tokens.

Conclusion

We should secure our app by checking for vulnerabilities and revoking tokens that were used for malicious purposes. Also, we need to take steps to prevent malicious from running on client and server-side by sanitizing data everywhere.

Finally, we should validate request bodies to make sure that valid data is submitted to our app.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *